package com.proyecto.swarm;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.json.JSONObject;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.model.BitmapDescriptor;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.MapFragment;
import com.proyecto.swarm.AntColonyOptimization.WalkedWay;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.graphics.Color;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;


public class MapaActivity extends Activity implements OnMarkerClickListener{
	
	private GoogleMap mapa;
	final LatLng sevilla = new LatLng(37.3907169, -5.9976047);	
	final LatLng NE = new LatLng(37.398460, -6.008972);
	final LatLng SW = new LatLng(37.377082, -5.986893);	
	
	private Marker icono;
	
	private BaseDatos bd;
	
	public static LinkedList<LatLng> coordenadas = new LinkedList<LatLng>();
	
	public static Map<String, Icono> iconos = new HashMap<String, Icono>();
	
	static Map<String, Boolean> visitas = new HashMap<String, Boolean>();
	
	public ProgressDialog pd;
	
	public Context contexto = this;
	

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mapa);
        
        mapa = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
		CameraUpdate camUpd2 = CameraUpdateFactory.newLatLngZoom(sevilla, 14.1f);
		mapa.getUiSettings().setZoomControlsEnabled(false);	
		mapa.setMyLocationEnabled(true);
		mapa.animateCamera(camUpd2);			
		
		bd = new BaseDatos(this);
        bd.abre();
        Cursor cur = bd.selectMonumentos();
        cur.moveToFirst();
        while (!cur.isAfterLast()) {
        	String clave = cur.getString(0);        	        	
        	//String imagenIcono = cur.getString(0) + ".png";  //Para acceder a un recurso en asset se necesita la extensión
        	String titulo = cur.getString(1); 
        	double latitud = cur.getDouble(2);
        	double longitud = cur.getDouble(3);
        	
        	//
        	//Dos formas de conseguir el id de un recurso. Uso la segunda forma porque es más rápida// 
        	//        	
        	//int id = getId(clave + "_nv_ico", R.drawable.class); //getId es un método propio
        	int idIconoNoVisitado = getResources().getIdentifier(clave + "_nv_ico", "drawable", getPackageName());
        	        	
        	LatLng vista = new LatLng(latitud, longitud);
        	
        	//
        	//Dos formas de añadir una imagen a un marker.
        	//Utilizando la carpeta res/drawable o utilizando la carpeta assets.
        	//El acceso a res/drawable es más rápido
        	//
        	icono = mapa.addMarker(new MarkerOptions().position(vista).title(titulo).icon(BitmapDescriptorFactory.fromResource(idIconoNoVisitado)));        	
        	//icono = mapa.addMarker(new MarkerOptions().position(vista).title(titulo).icon(BitmapDescriptorFactory.fromAsset(imagen)));
        	
        	Icono i = new Icono(clave, titulo, latitud, longitud);
        	iconos.put(titulo, i);
    		visitas.put(titulo, false);
        	
        	cur.moveToNext();        	
        }
        bd.cierra();
        
        mapa.setOnMarkerClickListener(this);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.mapa, menu);
        return true;
    }    


	@Override
	public boolean onMarkerClick(Marker marker) {
		icono = marker;		
		String titulo = marker.getTitle();
		
		Icono i = iconos.get(titulo);
		int foto = getResources().getIdentifier(i.clave, "drawable", getPackageName());
		int texto = getResources().getIdentifier(i.clave, "string", getPackageName());
		MonumentosInfo dialogo = MonumentosInfo.newInstance(titulo, foto, texto);
        dialogo.show(getFragmentManager(), "tagInfo");
		return false;
	}
	
	public void doVisitarClick(String titulo){
		if (!visitas.get(titulo)){
			LatLng posicion = icono.getPosition();			
			coordenadas.add(posicion);
			visitas.put(titulo, true);
			Icono iconoVisitado = iconos.get(titulo);
			int idIconoVisitado = getResources().getIdentifier(iconoVisitado.clave + "_ico", "drawable", getPackageName());
			BitmapDescriptor imagen = BitmapDescriptorFactory.fromResource(idIconoVisitado);
			icono.setIcon(imagen);	
			Log.d("Activity", String.valueOf(coordenadas.size()));
		}
	}
	
	public void doVolverClick(){
		
	}
	
	//
	//Método propio para encontrar el id de un recurso
	//
	public static int getId(String resourceName, Class<?> c) {
	    try {
	        Field idField = c.getDeclaredField(resourceName);
	        return idField.getInt(idField);
	    } catch (Exception e) {
	        throw new RuntimeException("No resource ID found for: " + resourceName + " / " + c, e);
	    }
	}
	
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
	    // Handle presses on the action bar items
	    switch (item.getItemId()) {
	        case R.id.action_settings:	            
	            return true;
	        case R.id.ruta:
	        	dialogoInicioRuta();
	            return true;
	        default:
	            return super.onOptionsItemSelected(item);
	    }
	}
	
	private void dialogoInicioRuta(){
		
		AlertDialog.Builder dialogo = new AlertDialog.Builder(this);
	     
        dialogo.setMessage("¿Quieres usar tu ubicación actual como inicio de la ruta?")
        .setTitle("Punto de inicio")
        .setCancelable(false)
        .setPositiveButton("Si", new DialogInterface.OnClickListener()  {        	
               public void onClick(DialogInterface dialog, int id) {                   
                    Location miPosicion = mapa.getMyLocation();
                    LatLng inicio = new LatLng(miPosicion.getLatitude(), miPosicion.getLongitude());
    				coordenadas.addFirst(inicio);
    				
    				calcularRuta();					
                    //dialog.cancel();
               }
        })
        .setNegativeButton("No", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {                   
                   
					calcularRuta();				
                   //dialog.cancel();
               }
        });
        dialogo.show();		
	}	
	
	private void calcularRuta(){			
		pd = ProgressDialog.show(this, "Procesando", "Espere unos segundos...", true, false); 
		ACOProcessing acoP = new ACOProcessing();
		acoP.execute();
	}	
	
	private class ACOProcessing extends AsyncTask<Void, Void, WalkedWay>{

		@Override
		protected WalkedWay doInBackground(Void... params) {
			AntColonyOptimization antColonyOptimization;
			WalkedWay walkedWay = null;
			try {
				antColonyOptimization = new AntColonyOptimization();			
				walkedWay = antColonyOptimization.start();			
			
			} catch (IOException e) {			
				e.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}			
			return walkedWay;				
		}
		
		@Override
		protected void onPostExecute(WalkedWay w){
			// Crear URL para enviar a Google Directions API
			pd.dismiss();
			String url = getDirectionsUrl(w.way);				
			
			pd = ProgressDialog.show(contexto, "Calculando ruta", "Espere unos segundos...", true, false);
			DownloadTask downloadTask = new DownloadTask();
		
			// Descargar los datos json desde Google Directions API
			downloadTask.execute(url);
			
		}		
	}
	
	private String getDirectionsUrl(int[] ruta){
		LatLng ini_fin = coordenadas.get(ruta[0]); //El mismo origen y destino porque el camino es circular. 
		//LatLng fin = coordenadas.getLast();
		
		// Inicio de ruta
		String str_origin = "origin=" + ini_fin.latitude + "," + ini_fin.longitude;
		
		// Final de ruta. (El mismo porque el camino es circular)
		String str_dest = "destination=" + ini_fin.latitude + "," + ini_fin.longitude;		
					
		// Sensor
		String sensor = "sensor=false";			
				
		// Waypoints
		String waypoints = "";
		for(int i = 1; i < ruta.length; i++){
			LatLng point = coordenadas.get(ruta[i]);
			if (i == 1) waypoints = "waypoints=";
			waypoints += point.latitude + "," + point.longitude + "|";
		}		
					
		// Construyendo los parametros
		String parameters = str_origin + "&" + str_dest + "&" + sensor + "&" + waypoints + "&units=metric&mode=walking";		
					
		// Formato de los datos
		String output = "json";
		
		// Construyendo la url
		String url = "https://maps.googleapis.com/maps/api/directions/" + output + "?" + parameters;		
		
		return url;
	}
	
	/** A method to download json data from url */
    private String downloadUrl(String strUrl) throws IOException{
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try{
            URL url = new URL(strUrl);

            // Creating an http connection to communicate with url 
            urlConnection = (HttpURLConnection) url.openConnection();

            // Connecting to url 
            urlConnection.connect();

            // Reading data from url 
            iStream = urlConnection.getInputStream();

            BufferedReader br = new BufferedReader(new InputStreamReader(iStream));

            StringBuffer sb = new StringBuffer();

            String line = "";
            while ((line = br.readLine()) != null){
            	sb.append(line);
            }
            
            data = sb.toString();

            br.close();

        }catch(Exception e){
            Log.d("Exception while downloading url", e.toString());
        }finally{
            iStream.close();
            urlConnection.disconnect();
        }
        return data;
     }	
	
	// Fetches data from url passed
	private class DownloadTask extends AsyncTask<String, Void, String>{			
				
		// Downloading data in non-ui thread
		@Override
		protected String doInBackground(String... url) {
				
			// For storing data from web service
			String data = "";
					
			try{
				// Fetching the data from web service
				data = downloadUrl(url[0]);
			}catch(Exception e){
				Log.d("Background Task", e.toString());
			}
			return data;		
		}
		
		// Executes in UI thread, after the execution of doInBackground()
		@Override
		protected void onPostExecute(String result) {			
			super.onPostExecute(result);			
			
			pd.dismiss();
			
			pd = ProgressDialog.show(contexto, "Dibujando ruta", "Espere unos segundos...", true, false);
			
			ParserTask parserTask = new ParserTask();
			
			// Invokes the thread for parsing the JSON data
			parserTask.execute(result);
				
		}		
	}
	
	/** A class to parse the Google Places in JSON format */
    private class ParserTask extends AsyncTask<String, Integer, List<List<HashMap<String, String>>>>{
    	
    	// Parsing the data in non-ui thread    	
		@Override
		protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) {		
			
			JSONObject jObject;	
			List<List<HashMap<String, String>>> routes = null;			           
            
            Log.d("URL", String.valueOf(jsonData[0].length()));
            
			try{
            	jObject = new JSONObject(jsonData[0]);
            	DirectionJSONParser parser = new DirectionJSONParser();
            	
            	// Starts parsing data
            	routes = parser.parse(jObject);    
            }catch(Exception e){
            	e.printStackTrace();
            }
            return routes;
		}
		
		// Executes in UI thread, after the parsing process
		@Override
		protected void onPostExecute(List<List<HashMap<String, String>>> result) {			
			
			ArrayList<LatLng> points = null;
			PolylineOptions lineOptions = null;
			
			// Traversing through all the routes
			for (int i = 0; i < result.size(); i++){
				points = new ArrayList<LatLng>();
				lineOptions = new PolylineOptions();
				
				// Fetching i-th route
				List<HashMap<String, String>> path = result.get(i);
				
				// Fetching all the points in i-th route
				for (int j = 0; j < path.size(); j++){
					HashMap<String, String> point = path.get(j);					
					
					double lat = Double.parseDouble(point.get("lat"));
					double lng = Double.parseDouble(point.get("lng"));
					LatLng position = new LatLng(lat, lng);						
					
					points.add(position);						
				}
				
				// Adding all the points in the route to LineOptions
				lineOptions.addAll(points);
				lineOptions.width(2);
				lineOptions.color(Color.RED);				
			}
			
			// Drawing polyline in the Google Map for the i-th route
			pd.dismiss();
			
			mapa.addPolyline(lineOptions);			
			
			bd.abre();			
			bd.insertarRuta(lineOptions);			
			bd.cierra();
			
			coordenadas.clear();
		}			
    }   
}
